package com;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.*;
import java.util.Arrays;

/**
 * @version: V1.0
 */

public class SM4Helper {

    public static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
    private static final String ALGORITHM = "SM4";
    //private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
    //银联标准PIN加密的时候要使用SM4/ECB/NoPadding
//    private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/NoPadding";
    private static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
    private static final String ENCODING = "UTF-8";
    private static final int KEY_SIZE = 128;
    private static final int MAC_SIZE = 16;

    static {
        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
        Security.addProvider(BOUNCY_CASTLE_PROVIDER);
    }

    public static void main(String[] args) {
        String encrypt = encrypt(generaterKey(), "15354067010");
        System.out.println(encrypt);
    }
    public static String generaterKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
            keyGenerator.init(KEY_SIZE, new SecureRandom());
            return encode(keyGenerator.generateKey().getEncoded());
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            //TODO
        }
        return null;
    }

    public static String encrypt(String key, String plainText) {
        try {
            return encode(encrypt(decode(key), plainText.getBytes(ENCODING)));
        } catch (IOException | DecoderException e) {
            //TODO
        }
        return null;
    }

    public static byte[] encrypt(byte[] key, byte[] plainText) {
        try {
            Cipher cipher = getCipher();
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, ALGORITHM));
            return cipher.doFinal(plainText);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String mac(String key, String plainText) {
        try {
            return encode(Arrays.copyOf(encrypt(decode(key), plainText.getBytes(ENCODING)), MAC_SIZE));
        } catch (IOException | DecoderException e) {
            //TODO
        }
        return null;
    }

    public static String decrypt(String key, String cipherText) {
        try {
            return new String(decrypt(decode(key), decode(cipherText)), ENCODING);
        } catch (IOException | DecoderException e) {
            //TODO
        }
        return null;
    }

    public static byte[] decrypt(byte[] key, byte[] cipherText) {
        try {
            Cipher cipher = getCipher();
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, ALGORITHM));
            return cipher.doFinal(cipherText);
        } catch (NoSuchAlgorithmException | BadPaddingException | InvalidKeyException | NoSuchPaddingException | NoSuchProviderException | IllegalBlockSizeException e) {
            //TODO
        }
        return null;
    }

    /**
     * encode
     *
     * @param bytes
     * @return
     */
    public static String encode(byte[] bytes) {
        return Hex.encodeHexString(bytes);
    }

    /**
     * decode
     *
     * @param str
     * @return
     * @throws IOException
     */
    public static byte[] decode(String str) throws DecoderException {
        return Hex.decodeHex(str.toCharArray());
    }

    private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
        return Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, BouncyCastleProvider.PROVIDER_NAME);
    }

}